﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Shapes;
using KSATouch;
using System.Windows.Threading;
using System.Threading;

namespace KSACalibrate
{

    /// <summary>
    /// Interaction logic for TouchCalibration.xaml
    /// </summary>
    public partial class TouchCalibration : Window
    {
        /// <summary>
        /// The label on the calibration page is set to be an offset amount of pixels 'in' from the top,bottom,left,and right 
        /// of the screen.  This offset should match that offset.
        /// </summary>
        const int CalOffset = 150;
        const int TouchEllipseRenderSize = 25;

        private Dictionary<int,Color> trackedContacts;

        enum CalibrationProgress {NotStarted,One,Two,Three,Four,Five,Six,Seven,Eight,Complete}

        private CalibrationProgress progress;
        private KSATouch.KSATouch ksaTouch;
        private int KinectID;
        private Random random;

        public TouchCalibration(KSATouch.KSATouch touchHandle, int KinectID)
        {
            InitializeComponent();
            
            progress = CalibrationProgress.NotStarted;
            this.Loaded += new RoutedEventHandler(TouchCalibration_Loaded);
            touchHandle.ContactDown += new ContactDownEventHandler(touchHandle_ContactDown);
            touchHandle.FrameReady += new FrameReadyEventHandler(touchHandle_FrameReady);
            this.ksaTouch = touchHandle;
            this.KinectID = KinectID;
            ksaTouch.DepthManagers[KinectID].Enabled = true;

            // Track and render contacts with random colors
            trackedContacts = new Dictionary<int, Color>();
            random = new Random((int)DateTime.Now.ToFileTime());            

            ksaTouch.DepthManagers[KinectID].DepthManagerSettings.CalibrationTopLeftDestination1 = new Point(CalOffset, CalOffset);
            ksaTouch.DepthManagers[KinectID].DepthManagerSettings.CalibrationBottomLeftDestination1 =
                new Point(CalOffset, SystemParameters.PrimaryScreenHeight - CalOffset);
            ksaTouch.DepthManagers[KinectID].DepthManagerSettings.CalibrationBottomRightDestination1 =
                new Point(SystemParameters.PrimaryScreenWidth - CalOffset, SystemParameters.PrimaryScreenHeight - CalOffset);
            ksaTouch.DepthManagers[KinectID].DepthManagerSettings.CalibrationTopRightDestination1
                = new Point(SystemParameters.PrimaryScreenWidth - CalOffset, CalOffset);

            ksaTouch.DepthManagers[KinectID].DepthManagerSettings.CalibrationTopLeftDestination2 = new Point(CalOffset, CalOffset);
            ksaTouch.DepthManagers[KinectID].DepthManagerSettings.CalibrationBottomLeftDestination2 =
                new Point(CalOffset, SystemParameters.PrimaryScreenHeight - CalOffset);
            ksaTouch.DepthManagers[KinectID].DepthManagerSettings.CalibrationBottomRightDestination2 =
                new Point(SystemParameters.PrimaryScreenWidth - CalOffset, SystemParameters.PrimaryScreenHeight - CalOffset);
            ksaTouch.DepthManagers[KinectID].DepthManagerSettings.CalibrationTopRightDestination2
                = new Point(SystemParameters.PrimaryScreenWidth - CalOffset, CalOffset);
            
            SetLabel(LabelOne); // start with the first one..
        
        }

        void touchHandle_FrameReady(object Sender, FrameReadyEventArgs e)
        {

            touchCanvas.Children.Clear();
            string ContactInfo = "Count: " + ksaTouch.CurrentContacts.Count.ToString() + "\r\n";
            foreach (Contact contact in ksaTouch.CurrentContacts)
            {
                // Render the contact in a simple form
                Color color;

                if (trackedContacts.Keys.Contains(contact.Id))
                { 
                    color = trackedContacts[contact.Id];
                }
                else
                {
                    color = GetRandomColor();
                    trackedContacts.Add(contact.Id, color);
                }

                Ellipse ellipse = new Ellipse();
                ellipse.Width = TouchEllipseRenderSize;
                ellipse.Height = TouchEllipseRenderSize;
                ellipse.Fill = new SolidColorBrush(color);
                Canvas.SetTop(ellipse, contact.CenterPoint.Y + (TouchEllipseRenderSize / 2));
                Canvas.SetLeft(ellipse, contact.CenterPoint.X + (TouchEllipseRenderSize / 2));
                touchCanvas.Children.Add(ellipse);

                ContactInfo += "ID: " + contact.Id + "\r\n";
                ContactInfo += "Points: " + contact.Points.Count + "\r\n";
                ContactInfo += "Width: " + contact.ContactBlob.BoundingBox.Width + "\r\n";
                ContactInfo += "Height: " + contact.ContactBlob.BoundingBox.Height + "\r\n";
                ContactInfo += "X: " + contact.ContactBlob.CenterPoint.X + "\r\n";
                ContactInfo += "Y: " + contact.ContactBlob.CenterPoint.Y + "\r\n";
                //ContactInfo += "Height: " + contact.ContactBlob.CenterPoint..Height + "\r\n";

                if (contact.CenterPoint != null)
                {
                    ContactInfo += "Actual X: " + contact.CenterPoint.X + "\r\n";
                    ContactInfo += "Actual Y: " + contact.CenterPoint.Y + "\r\n";
                }

                ContactInfo += "\r\n";
            }
                       

            // Cheap invoker to get our data back to the UI thread
            Dispatcher.BeginInvoke(DispatcherPriority.Normal, new ThreadStart(() => 
                { 
                    TouchInfo.Content = ContactInfo;
                }));

            // another cheap trick...every time there are no contacts, then clear our tracked ones. 
            if (ksaTouch.CurrentContacts.Count == 0)
            {
                trackedContacts.Clear();
            }
        }

        void touchHandle_ContactDown(object sender, ContactEventArgs e)
        {
           
        }

        void TouchCalibration_Loaded(object sender, RoutedEventArgs e)
        {
            progress = CalibrationProgress.One;

            this.MouseRightButtonDown += new MouseButtonEventHandler(TouchCalibration_MouseDown);
        }

        void TouchCalibration_MouseDown(object sender, MouseButtonEventArgs e)
        {
            if (ksaTouch.CurrentContacts.Count != 1)
            {
                if (ksaTouch.CurrentContacts.Count == 0)
                {
                    HelppLabel.Content = "I'm sorry, but the Kinect was unable to see your finger. Please try placing it flatter against the screen, and try again.";
                }
                else
                {
                    HelppLabel.Content = "We saw too many contacts to get a solid lock on a coordinate. Please try again. Number of contacts seen: " + ksaTouch.CurrentContacts.Count;
                }
                
            }
            else
            {
                HelppLabel.Content = "Continue to place a finger over the red highlighted number, and right click...";
                Point touchPoint = new Point(
                    ksaTouch.CurrentContacts.First().ContactBlob.CenterPoint.X,
                    ksaTouch.CurrentContacts.First().ContactBlob.CenterPoint.Y);
                switch (progress)
                {
                        // 'flip' calibration points since the depth field is currently flipped for x/y. 
                    case CalibrationProgress.Four:
                        {
                            // Right top point should be moved in and down based on the boundary rect
                            ksaTouch.DepthManagers[KinectID].DepthManagerSettings.CalibrationTopLeftSource1 = new Point(
                                touchPoint.X,
                                 touchPoint.Y);
                             
                           
                            break;
                        }
                    case CalibrationProgress.Three:
                        {
                            ksaTouch.DepthManagers[KinectID].DepthManagerSettings.CalibrationBottomLeftSource1 = new Point(
                                touchPoint.X,
                                touchPoint.Y);  

                            break;
                        }
                    case CalibrationProgress.Two:
                        {
                            ksaTouch.DepthManagers[KinectID].DepthManagerSettings.CalibrationBottomRightSource1 = new Point(
                                touchPoint.X,
                                touchPoint.Y);

                            break;
                        }
                    case CalibrationProgress.One:
                        {
                            ksaTouch.DepthManagers[KinectID].DepthManagerSettings.CalibrationTopRightSource1 = new Point(
                                 touchPoint.X,
                                 touchPoint.Y);
                            break;
                        }
                    // Upside down calibration
                    case CalibrationProgress.Eight:
                        {
                            ksaTouch.DepthManagers[KinectID].DepthManagerSettings.CalibrationBottomRightSource2 = new Point(
                                 touchPoint.X,
                                 touchPoint.Y);
                            break;
                        }
                    case CalibrationProgress.Seven:
                        {
                            ksaTouch.DepthManagers[KinectID].DepthManagerSettings.CalibrationTopRightSource2 = new Point(
                                 touchPoint.X,
                                 touchPoint.Y);
                            break;
                        }
                    case CalibrationProgress.Six:
                        {
                            ksaTouch.DepthManagers[KinectID].DepthManagerSettings.CalibrationTopLeftSource2 = new Point(
                                 touchPoint.X,
                                 touchPoint.Y);
                            break;
                        }
                    case CalibrationProgress.Five:
                        {
                            ksaTouch.DepthManagers[KinectID].DepthManagerSettings.CalibrationBottomLeftSource2 = new Point(
                                 touchPoint.X,
                                 touchPoint.Y);
                            break;
                        }
                }
                                    
            }
            
            NextState();
        }

        private void NextState()
        {
            switch (progress)
            { 
                case CalibrationProgress.NotStarted :
                    {
                        progress = CalibrationProgress.One;
                        SetLabel(LabelOne);
                        break;
                    }
                case CalibrationProgress.Five:
                    {
                        progress = CalibrationProgress.Six;
                        SetLabel(LabelTwo);
                        break;
                    }
                case CalibrationProgress.One:
                    {
                        progress = CalibrationProgress.Two;
                        SetLabel(LabelTwo);
                        break;
                    }
                case CalibrationProgress.Six:
                    {
                        progress = CalibrationProgress.Seven;
                        SetLabel(LabelThree);
                        break;
                    }
                case CalibrationProgress.Two:
                    {
                        progress = CalibrationProgress.Three;
                        SetLabel(LabelThree);
                        break;
                    }
                case CalibrationProgress.Seven:
                    {
                        progress = CalibrationProgress.Eight;
                        SetLabel(LabelFour);
                        break;
                    }
                case CalibrationProgress.Three:
                    {
                        progress = CalibrationProgress.Four;
                        SetLabel(LabelFour);
                        break;
                    }
                case CalibrationProgress.Eight:
                    {
                        progress = CalibrationProgress.Complete;
                        SetLabel(null);
                        ksaTouch.DepthManagers[KinectID].DepthManagerSettings.SaveSettings();
                        MessageBox.Show("Saved Calibration!");
                        break;
                    }
                case CalibrationProgress.Four:
                    {
                        progress = CalibrationProgress.Five;
                        SetLabel(LabelOne);
                        break;
                    }
            }
        }

        /// <summary>
        /// Set all labels to blue, but set the active to 'red'
        /// </summary>
        /// <param name="active"></param>
        private void SetLabel(Label active)
        {
            LabelOne.Foreground = Brushes.Blue;
            LabelTwo.Foreground = Brushes.Blue;
            LabelThree.Foreground = Brushes.Blue;
            LabelFour.Foreground = Brushes.Blue;

            if (active == null)
                return;

            active.Foreground = Brushes.Red;
        }

        private void ExitButton_Click(object sender, RoutedEventArgs e)
        {            
            this.Close();
        }

        private void SaveButton_Click(object sender, RoutedEventArgs e)
        {
            //ksaTouch.DepthManager.DepthManagerSettings.SaveSettings();
        }

        /// <summary>
        /// Toggle the window style on double click..this allows us to move this window to another monitor/projector/etc.
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void Window_MouseDoubleClick_1(object sender, MouseButtonEventArgs e)
        {
            if (this.WindowState == System.Windows.WindowState.Maximized)
            {
                this.WindowState = System.Windows.WindowState.Normal;
                this.WindowStyle = System.Windows.WindowStyle.ThreeDBorderWindow;
            }
            else
            {
                this.WindowState = System.Windows.WindowState.Maximized;
                this.WindowStyle = System.Windows.WindowStyle.None;
            }
        }
        public Color GetRandomColor()
        {
            Color color = new Color();
            color.R = (byte)random.Next(25,250);
            color.G = (byte)random.Next(25,250);
            color.B = (byte)random.Next(25,250);
            color.A = 255;
            return color;
        }
    }
}
